import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Vector;
import java.util.concurrent.Semaphore;

import sun.awt.Mutex;


public class QuadrotorServer {
	private ArrayList<ControlSession> runningSessions;
	private Semaphore controlSessionEdit = new Semaphore(1);
	
	private final static int controllerPort = 6789;
	private final static int vehiclePort = 6790;

	private static ServerSocket ctrlServ;
	private static ServerSocket vehServ;
	
	private final static ServerGUI server = new ServerGUI();
	
	public QuadrotorServer() {
		//set up the runningSessions list
		runningSessions = new ArrayList<ControlSession>();
		//set up the sockets
		boolean youFailed = false;
		try {
			ctrlServ = new ServerSocket(controllerPort, 8);
		} catch (IOException e) { e.printStackTrace(); youFailed = true; }
		try {
			vehServ = new ServerSocket(vehiclePort, 8);
		} catch (IOException e) { e.printStackTrace(); youFailed = true; }
		if(youFailed) { System.exit(1); }
		
		//set up and start a thread to update the GUI state
		Thread guiMan = new Thread() {
			public void run() {
				while( !server.getStop() ) {
					//do a blocking wait on the runningSessions semaphore
					try { controlSessionEdit.acquire(); } catch (InterruptedException e) { e.printStackTrace(); continue; }
					//iterate over the ControlSessions in the runningSessions list and throw out ones which have terminated
					int runSesnIter = 0;
					while(runSesnIter < runningSessions.size()) {
						if(runningSessions.get(runSesnIter).isStopped()) {
							server.removeControlSession( runningSessions.get(runSesnIter).getPubName() );  //take the name off the GUI
							runningSessions.remove(runSesnIter);
						}else { runSesnIter++; }
					}
					//release the runningSessions semaphore
					controlSessionEdit.release();
				}
			}
		};
		guiMan.start();
		//TODO this could be made to stash up incoming connections but that is hardly necessary with a backlog
		//set up and start a thread to get incoming connections and add them to the ControlSession list
		Thread connMan = new Thread() {
			public void run() {
				while( !server.getStop() ) {
					if( server.getOrSetAcceptConnect(false, true) ) {
						Socket vehSock = null, ctrlSock = null;
						ControlSession newCSesn = null;
						//await a vehicle connection (note - continue if we're interrupted. 
						try { vehSock = vehServ.accept(); } catch (IOException e) { System.out.println(); continue; }
						//await a controller connection
						while( server.getOrSetAcceptConnect(false, true) && vehSock.isConnected() ) {
							try { ctrlSock = ctrlServ.accept(); break; } catch(IOException e) { System.out.println(); continue; }
						}
						//use both sockets to create a new ControlSession
						if(vehSock.isConnected() && ctrlSock.isConnected()) {
							newCSesn = new ControlSession(ctrlSock, vehSock);
							//take the semaphore so we can add to the runningSessions list
							try { controlSessionEdit.acquire(); }
							catch (InterruptedException e) {
								if( vehSock.isConnected() ) { try{ vehSock.close(); }catch(Exception eG){ System.out.println("failed to close new vehsock"); } }
								if( ctrlSock.isConnected() ) { try{ ctrlSock.close(); }catch(Exception eG){ System.out.println("failed to close new ctrlsock"); } }
								e.printStackTrace();
								continue;
							}
							//add the new ControlSession to runningSessions
							runningSessions.add(newCSesn);
							//update the GUI with the information about the new ControlSession
							Vector<String> newSessGuiInfo = newCSesn.getPubName();
							server.addControlSession(newSessGuiInfo);
							//release the semaphore
							controlSessionEdit.release();
						}
						else { //one of the sockets is closed now. try to close any open ones left and wait for a new connection
							if( vehSock.isConnected() ) { try{ vehSock.close(); }catch(Exception e){ System.out.println("failed to close new vehsock"); } }
							if( ctrlSock.isConnected() ) { try{ ctrlSock.close(); }catch(Exception e){ System.out.println("failed to close new ctrlsock"); } }
							continue;
						}
					}
					else { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } }
				}
			}
		};
		connMan.start();
		//try to join all the threads
		try {
			guiMan.join();
			vehServ.close();
			ctrlServ.close();
			//closing the serversockets interrupts any work connMan was doing
			connMan.join();
		} catch (Exception e) { e.printStackTrace(); System.exit(0); }
	}
	
	public static void main(String args[]) {
		QuadrotorServer qsrInstance = new QuadrotorServer();
	}

}
